import {Vector} from "./Vector";
import {IVector2, IVector3} from "./types";
import {Matrix4} from "./Matrix4";
import {Vector2} from "./Vector2";
import {Vector4} from "./Vector4";
/*
Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) {
    Vec3f s[2];
    for (int i=2; i--; ) {
        s[i][0] = C[i]-A[i];
        s[i][1] = B[i]-A[i];
        s[i][2] = A[i]-P[i];
    }
    Vec3f u = cross(s[0], s[1]);
    if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate
        return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z);
    return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator
}
*/
export class Vector3 extends Vector  implements IVector3{

    /**
     * 计算三角形的差值
     * @param A
     * @param B
     * @param C
     * @param P
     *
     */


    constructor(x: number = 0, y:number = 0, z: number = 0) {
        super(3);
        this.x = x;
        this.y = y;
        this.z = z;
    }

    loadData(data: IVector3) {
        this.x = data.x;
        this.y = data.y;
        this.z = data.z;
        return this;
    }
    set x(value: number) {
        this.data[0] = value;
    }
    get x() {
        return this.data[0];
    }
    set y(value: number) {
        this.data[1] = value;
    }
    get y() {
        return this.data[1];
    }
    set z(value: number) {
        this.data[2] = value;
    }
    get z() {
        return this.data[2];
    }

    cross(target: IVector3) {
        return Vector3.crossVector(this, target);
    }

    dot(target: IVector3) {
        return Vector3.docVector(this, target);
    }

    static docVector(v1: IVector3, v2: IVector3) {
        return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
    }

    static crossVector(v1: IVector3, v2: IVector3): Vector3 {
        // y1z2-z1y2，x2z1-x1z2，x1y2-y1x2
        return new Vector3(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x-v1.x*v2.z, v1.x*v2.y-v1.y*v2.x);
    }

    added(target: IVector3)  {
        return new Vector3(this.x + target.x, this.y + target.y, this.z + target.z);
    }
    add(target: IVector3)  {
        this.x += target.x;
        this.y += target.y;
        this.z += target.z;
        return this;
    }

    subed(target: IVector3) {
        return new Vector3(this.x - target.x, this.y-target.y, this.z-target.z);
    }

    multiplied(scale: number) {
        return new Vector3(this.x * scale, this.y * scale, this.z * scale);
    }
    /**
     * Multiplies this vector by scalar s.
     */
    multiplyScalar( scalar: number ): this {

        this.x *= scalar;
        this.y *= scalar;
        this.z *= scalar;

        return this;

    }
    multipliedXYZ(xScale: number = 1, yScale  = 1, zScale:number = 1) {
        return new Vector3(this.x * xScale, this.y * yScale, this.z * zScale);
    }

    /**
     * Computes squared length of this vector.
     */
    lengthSq(): number {

        return this.x * this.x + this.y * this.y + this.z * this.z;

    }

    /**
     * Sets this vector to a - b.
     */
    subVectors( a: IVector3, b: IVector3 ): this {

        this.x = a.x - b.x;
        this.y = a.y - b.y;
        this.z = a.z - b.z;

        return this;

    }

    /**
     * Divides this vector by scalar s.
     * Set vector to ( 0, 0, 0 ) if s == 0.
     */
    divideScalar( s: number ): this {

        return this.multiplyScalar( 1 / s );

    }

    /**
     * Computes length of this vector.
     */
    length(): number {

        return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );

    }
    /**
     * Normalizes this vector.
     */
    normalize(): this {

        return this.divideScalar( this.length() || 1 );

    }

    /**
     * Sets this vector to cross product of a and b.
     */
    crossVectors( a: IVector3, b: IVector3 ): this {

        let ax = a.x, ay = a.y, az = a.z;
        let bx = b.x, by = b.y, bz = b.z;

        this.x = ay * bz - az * by;
        this.y = az * bx - ax * bz;
        this.z = ax * by - ay * bx;

        return this;

    }

    applyMatrix4( m: Matrix4 ): this {

        let x = this.x, y = this.y, z = this.z;
        let e = m.elements;

        let w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );

        this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
        this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
        this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;

        return this;

    }

    appliedMatrix4( m: Matrix4 ): Vector3 {
        let x = this.x, y = this.y, z = this.z;
        let e = m.elements;

        let w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );

        const re = new Vector3();
        re.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
        re.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
        re.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;

        return re;
    }
    applyVectorMatrix4( m: Matrix4 ): this {

        let x = this.x, y = this.y, z = this.z;
        let e = m.elements;

        let w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z );

        this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z ) * w;
        this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z) * w;
        this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z) * w;

        return this;

    }

    appliedVectorMatrix4( m: Matrix4 ) {
        let x = this.x, y = this.y, z = this.z;
        let e = m.elements;

        let w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z  );

        const re = new Vector3();
        re.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z  ) * w;
        re.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z ) * w;
        re.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z ) * w;

        return re;
    }

    toVector4(w: number = 1) {
        return new Vector4(this.x, this.y, this.z, w);
    }

}